package hyl.ext.ws;

import java.util.List;
import java.util.Map;

import javax.websocket.Session;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson2.JSONObject;

import hyl.core.MyDate;
import hyl.core.MyFun;
import hyl.core.data.ExMap2;
import hyl.core.net.pg.PGD;
import hyl.ext.base.MySession;
import hyl.ext.base.SessionFactory;
import hyl.ext.ws.msg.Msgpg;
import hyl.ext.ws.msg.MsgpgStream;

/*
 * 客服模型对象
 * 不缓存客户聊天记录
 * 适用于 多企业 客服
 * 
 * 客服需要登录, 游客不需要登录,
 * 服务端实现消息的中转
 * 
 * 一个客服只能有一个终端在线,如果有第二个终端 第一个终端会被迫下线
 * 
 * 客服账号不能改变公司, 如果职工跳槽 需要重新注册新的客服账号
 * 
 * 客服登录, 自动归入公司群
 * 客服离开, 自动离开公司群
 * 
 * 游客选择客服后弹出聊天室窗口.
 * 对于客服,每个(客服+游客)聊天都是一个窗口,
 * 对于游客, 所有客服共用一个窗口
 * 
 * 该窗口保留聊天记录到文件日志 文件最多保留n天 
 * 
 * 	配置项 system.ini 聊天记录最多保留=n
 * 
 * 只能是游客和客服之间聊天 ,客服客服之间聊天 ,游客之间不能聊天
 * 
 * 
 * 注意: 
 * 1.客户终端id 必须是字符串格式 与 用户id integer编码不同 
 * 2.与 WsServe2,WsServe3 主题名称 必须不同,否则会串扰
 * 
 * @author 邹工 37798955@qq.com
 *
 */

public class WsServeK extends WsServe {
	public static final Logger Log = LoggerFactory.getLogger(WsServeK.class);
	// 静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。

	// private String basemsg = "";

	/**
	 * 客户端编号， 只有客服可以使用的变量
	 */
	protected WsRoom _客服房间 = null;
	protected String _组织ID = null;// 非必填
	public static final String ID系统 = "XT";
	public static final int D系统 = 0;
	public static final int D客服 = 1;
	public static final int D游客 = 2;
	public static final int D客户 = 3;
	protected int _角色 = D客服; // 客服
	/**
	 * key: 客服id+客户id, value: 客服id
	 */

	public static final int I会话过期 = 3600000;
	// 令牌生成器 ??
	// public static ExTokenFactory _ef = new ExTokenFactory(I会话过期);// 一小时过期
	/**
	 * key :公司 field 客服 value:在线状态
	 * 
	 * 客服房间不能删除 把这些房间编号存到redis中 防止丢失
	 */
	public static ExMap2<String, String, Object[]> _在线客服集 = new ExMap2<>(I会话过期);
	/*
	 * 系统启动以后存在一个系统账号 与企业公司无关 虚拟一个系统账号 用来响应 客服的请求指令
	 */
//	public static final WsServeK 系统账号 = new WsServeK();
//	static {
//		// 虚拟一个系统账号 用来响应 客服的请求指令
//		// 组织机构, 系统编号, 头像 ,名称
//		 系统账号.loginByKf("1", ID系统, "../../img/chat/meactive.png", "系统");
//	}

	/**
	 * 系统发送文本消息的函数
	 * 
	 * @param code
	 * @param msg
	 * @return
	 */

	public synchronized boolean send系統消息(String 主题, int code, String msg) {
		Msgpg pg = Msgpg.create系统消息(ID系统);
		JSONObject job = new JSONObject();
		job.put("msg", msg);
		job.put("code", code);
		pg.setMsg(_clientid, 主题, job.toJSONString());
		return sendTo(pg);
	}

	protected WsRoom getRoom(String roomid) {

		WsRoom wc = null;
		if (roomid != null) {
			wc = _在线房间集.get(roomid);
		}
		if (wc == null) {// 如果房间不存在,就立即创建一个
			wc = new WsRoom(roomid, WsRoom.D客服);
			_在线房间集.put(roomid, wc);
		} else {// 如果不为空说明已经存在房间
			//MyFun.print(wc);
			if (!WsRoom.D客服.equals(wc._模型)) {
				send系統消息(PGD.S_登录.getS(), 0, roomid + "编号已被其他模型使用");
				close();
				return null;
			}
		}
		return wc;
	}

	/**
	 * (客服会话登录)<br>
	 * 
	 * 用户必须先登录后台 然后使用登录会话的令牌登录
	 * 
	 * 否则阻止用户连接websocket
	 * 
	 * @param token   登录会话的令牌
	 * @param session 可选的参数。session为与某个客户端的ws连接会话，需要通过它来给客户端发送数据
	 */
	public void loginBySession(String token, Session session) {
		if (MyFun.isEmpty(token)) {
			send系統消息(PGD.S_登录.getS(), 0, "令牌为空异常");
			close();
			return;
		}
		// 判断该用户登录了吗,如果登录了,才能连接
		if (SessionFactory.isLogin(token)) {
			this._token = token;
			MySession ms = SessionFactory.getSessionById(token);
			if (MyFun.isEmpty(ms.getOrgid())) {
				send系統消息(PGD.S_登录.getS(), 0, "所在机构为空异常");
				close();
				return;
			}
			if (_角色 == D客服)
				// 只有客服才会登录,普通游客无须登录
				if (ms.getRoleid() == 1 || ms.getRoleid() == 114)
					loginByKf(String.valueOf(ms.getOrgid()), ms.userIdToStr(), ms.get头像(), ms.getUname());
				else if (_角色 == D客户)
					loginByKh(String.valueOf(ms.getOrgid()), ms.userIdToStr(), ms.get头像(), ms.getUname());
			return;
		}
		send系統消息(PGD.S_登录.getS(), 0, String.format("该令牌已过期,ip=%s", _ip));
		close();
		return;
	}

	public String do拼接客服id(String 组织, String 用户id) {
		return MyFun.join("kf-", 组织, "-", 用户id);
	}

	/**
	 * 
	 * (简单的登录方式 ,调试用)
	 * 
	 * id 方式登录 如果有相同的id 就把他踢下去,
	 * 
	 * 这种登录方式 主要用于 二次登录
	 * 
	 * @param id      身份ID主题
	 * @param session
	 */

	public void loginByKf(String 组织, String 用户id, String 用户头像, String 用户名) {
		_clientid = do拼接客服id(组织, 用户id);
		_组织ID = 组织;
		_在线客服集.put(组织, _clientid, new Object[] { 用户id, 用户名, 用户头像 });
		// MyFun.print(组织,用户id,_客服集);
		_角色 = D客服;
		// 如果会员编号为0 , 关闭当前连接 ,退出
		// 如果不为0 ,且 该编号已经存在,关闭当前连接 ,退出
		if (MyFun.isEmpty(_clientid)) {
			send系統消息(PGD.S_登录.getS(), 0, "发现异常客户端0,ip=" + _ip);
			close();
			return;
		}

		// 创建客服房间
		_客服房间 = getRoom(_clientid);
		if (_客服房间 == null) {
			return;
		}
		reg终端();
		// 登录后的返回内容
		JSONObject me = new JSONObject();
		me.put("name", 用户名);
		me.put("head", 用户头像);
		me.put("id", _clientid);
		// 提取离线后的未阅留言
		List<Msgpg> list = _客服房间.loadMsgpg();
		byte[] dat = _客服房间.load日消息();
		MsgpgStream wmbs = MsgpgStream.getInstance();
		List<Msgpg> log = wmbs.add碎片(dat).toList();
		JSONObject job = new JSONObject();
		job.put("his", list);
		job.put("log", log);
		job.put("me", me);
		job.put("code", 1);
		String s = job.toJSONString();
		//MyFun.print(s);
		Msgpg pg = Msgpg.create系统消息(ID系统);
		pg.setMsg(_clientid, PGD.S_登录.getS(), s);
		byte[] bb = pg.toSendBytes();
		this.sendTo(bb);
		// basemsg = "id:" + _clientid + ",_ip:" + _ip;
	}

	/**
	 * 游客登录
	 * 
	 * @param 组织id
	 */
	public void loginByYk(String 组织id) {//, String 编号
		//指定游客编号 没有意义,为了防止被黑客攻击	,游客刷新后,必须发起离线消息???
		_clientid = MyFun.join("yk-", 组织id, "-", _Ws会话.getId());	
		//MyFun.print(组织id,  _clientid);
		_组织ID = 组织id;
		_角色 = D游客;
		reg终端();
		// 游客登录后 返回内容
		回调客服集合();
	}

	// ??
	public void loginByKh(String 密文) {
		// MyRsa.
	}

	/**
	 * 用户登录
	 */
	public void loginByKh(String 组织, String 用户id, String 头像, String 用户名) {
		_clientid = MyFun.join("kh-", 组织, "-", 用户id);
		_角色 = D客户;
		_组织ID = 组织;
		reg终端();
		// 游客登录后 返回内容
		回调客服集合();

	}

	void 回调客服集合() {
		Map<String, Object[]> 客服map = _在线客服集.getAll(_组织ID);
		// MyFun.printJson(客服map);
		JSONObject job = new JSONObject();
		job.put("kf", 客服map);
		job.put("me", _clientid);
		job.put("code", 1);
		String s = job.toJSONString();
		///MyFun.print(s);
		Msgpg pg = Msgpg.create系统消息(ID系统);
		pg.setMsg(_clientid, PGD.S_登录.getS(), s);
		byte[] bb = pg.toSendBytes();
		// MyFun.print(bb.length);
		this.sendTo(bb);
	}

	/**
	 * 终端 保存到在线终端集合 每个clientid 只能有一个在线会话
	 */
	void reg终端() {
		if (_在线终端集.containsKey(_clientid)) {
			// 关闭原来的socket ,重新建立连接, 把原来的客户踢下去
			WsServeK it = (WsServeK) _在线终端集.get(_clientid);
			it.close();
			it = null;
			// 先删除后添加
		}
		_在线终端集.put(_clientid, this);
		// MyFun.printJson(WsServe._在线终端集);
	}

	/**
	 * 连接后关闭调用的方法
	 */
	public void onClose() {
		if (_clientid == null)
			return;
		_在线终端集.remove(_clientid);
		_在线客服集.remove(_组织ID, _clientid);
	}

	/*
	 * 游客 发送的消息 如果客服不在,保留到客服房间
	 * 
	 * 客服发送的消息 如果游客不在, 反馈游客已经离线
	 * 
	 * 
	 */
	/**
	 * 消息中转
	 * 
	 * 消息只能在 游客和客服之间中转
	 * 
	 * 
	 * @param 消息
	 * @return
	 */
	public void on消息处理(Msgpg 消息, Session session) {
		if (消息.接收方.equals(ID系统)) {
			系统处理客户请求指令(消息, session);
			return;
		}
		WsServe se = _在线终端集.get(消息.接收方);
		// if (系统账号.equals(消息.接收方)) {}
		String roomid = null;
		// 游客没有房间客服有, 游客使用客服的房间记录消息
		// 如果当前角色是游客, 根据消息锁定当前房间
		if (_角色 == D游客) {
			roomid = 消息.接收方;
		} else if (_角色 == D客服) {
			roomid = _clientid;
		}
		WsRoom wc = getRoom(roomid);
		if (wc == null) {
			return;
		}
		// wc 肯定不为空 ,se为空表示 对方离线了
		if (se == null) {
			Msgpg pg2 = Msgpg.create系统消息(ID系统);
			// 如果是游客,当客服不在线时存储消息,保留消息,等待客服登录后
			if (_角色 == D游客) {
				wc.saveMsgpg(消息);
				wc.save日消息(MyDate.getNow(), 消息.toSendBytes());
				pg2.setMsg(_clientid, PGD.S_退出.getS(), 消息.接收方, MyFun.u8str2Bytes("客服离线了,已留言"));
				// MyFun.print(pg2);
			} else {// 客服发送 游客离线
				// 游客不在线时不存储消息,即使存储
				pg2.setMsg(_clientid, PGD.S_退出.getS(), 消息.接收方, MyFun.u8str2Bytes("游客已离线"));
			}
			// MyFun.print(pg2);
			// 回复消息
			this.sendTo(pg2);

		} else {
			// 转发消息
			// MyFun.printJson(消息);
			se.sendTo(消息);
			wc.save日消息(MyDate.getNow(), 消息.toSendBytes());
		}

	}

	void 系统处理客户请求指令(Msgpg 消息, Session session) {
//		if (消息.主题.equals(PGD.K_更新客服.getS())) {
//		Map<String, String> 客服map = _客服集.getAll(_组织ID);
//		String s = JSONObject.toJSONString(客服map);
//		MyFun.print(s);
//		Msgpg pg = new Msgpg();
//		pg.setMsg(_clientid, PGD.K_更新客服.getS(), s);
//		se.sendTo(pg);
//	} else {	
	}

	/**
	 * 
	 * @param 消息
	 * @param session
	 */
	void 存储消息(Msgpg 消息, Session session) {

	}

	public static void closeAll() {
		_在线终端集.forValue((v) -> v.close());
	}

}